﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.DirectoryServices.Protocols;
using System.Security.Principal;

namespace ADSync.ActiveDirectory
{
	/// <summary>
	/// Class delivers set of AD operations
	/// Translates attributes, retrieves multi-valued attributes
	/// and executes change requests
	/// </summary>
	public class Operations : IDisposable
	{
		#region Properties
		private bool bDisposed;
		#endregion
		
		#region Public Methods

		/// <summary>
		/// Method retrieves translated single and multi-valued attribute value(s)
		/// Designed for sidHistory, tokenGroups, member and memberOf
		/// May support other attributes although it's not its primary function.
		/// </summary>
		/// <param name="ObjectDistinguishedName"></param>
		/// <param name="AttributeName"></param>
		/// <returns></returns>
		public HashSet<String> GetAttributeValues(String ObjectDistinguishedName, String AttributeName)
		{
			
		}

		#endregion
		#region Private Methods
		private void GetTokenGroups(LdapConnection oLdapConn, string sObjectDn, ref HashSet<string> hsGroups, bool bAddOwnSid)
		{
			try
			{
				SearchResponse dirRes = null;
				SearchResultEntry srEntry = null;

				string sFilterBase = @"(|(&(objectClass=user)(objectCategory=person))(&(objectClass=group)(objectCategory=group)))";

				string[] aAttributesToAdd = bAddOwnSid
						? new string[] { "tokenGroups", "objectSid" }
						: new string[] { "tokenGroups" };

				dirRes = (SearchResponse)oLdapConn.SendRequest(new SearchRequest(
					sObjectDn,
					String.Format("(&{0})", sFilterBase),
					System.DirectoryServices.Protocols.SearchScope.Base,
					aAttributesToAdd
					));

				if (dirRes.Entries.Count > 0)
				{
					srEntry = dirRes.Entries[0];
					if (srEntry.Attributes.Contains("tokenGroups"))
					{
						DirectoryAttribute oAttribute = srEntry.Attributes["tokenGroups"];

						object[] aValues = oAttribute.GetValues(Type.GetType("System.Byte[]"));
						for (int i = 0; i < aValues.Length; i++)
						{
							try
							{
								SecurityIdentifier oSID = new SecurityIdentifier((byte[])aValues[i], 0);
								string sObjectSid = oSID.Value;
								if (!hsGroups.Contains(sObjectSid))
								{
									hsGroups.Add(sObjectSid);
								}
							}
							catch
							{
								;
							}
						}
					}
					if (bAddOwnSid)
					{
						DirectoryAttribute oAttribute = srEntry.Attributes["objectSid"];

						object[] aValues = oAttribute.GetValues(Type.GetType("System.Byte[]"));
						SecurityIdentifier oSID = new SecurityIdentifier((byte[])aValues[0], 0);
						string sObjectSid = oSID.Value;
						if (!hsGroups.Contains(sObjectSid))
						{
							hsGroups.Add(sObjectSid);
						}
					}
				}
			}
			catch (Exception Ex)
			{
				throw new Exception(string.Format("{0}::{1}", new StackFrame(0, true).GetMethod().Name, Ex.Message));
			}
		}

		/// <summary>
		/// Method translates attribute into string collection
		/// Works for single and multi-valued attributes
		/// Single-valued attribute will result in one collection entry
		/// ISSpyra
		/// </summary>
		/// <param name="ObjectAttribute"></param>
		/// <returns></returns>
		private IEnumerable<String> TranslateAtribute(DirectoryAttribute ObjectAttribute)
		{
			Type AttributeType = ObjectAttribute[0].GetType();

			//Type ElementType = ( (Object[])ObjectAttribute[0])[0].GetType();
			if( Type.GetTypeCode(AttributeType) == TypeCode.Byte )
			{
				object[] AttributeValues = ObjectAttribute.GetValues(Type.GetType("System.Byte[]"));


				if(ObjectAttribute.Name.Equals("objectGuid", StringComparison.OrdinalIgnoreCase))
				{	
					yield return (new Guid((byte[])AttributeValues[0])).ToString();
				}
				else
				{
					for (int i = 0; i < AttributeValues.Length; i++)
					{
						yield return (new SecurityIdentifier((byte[])AttributeValues[i], 0)).Value;
					}
				}
			}
			else
			{
				object[] AttributeValues = ObjectAttribute.GetValues(AttributeType);
				foreach (object SingleValue in AttributeValues)
				{
					yield return SingleValue.ToString();
				}
			}
		}
		#endregion

		#region IDisposable Members
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool bDisposing)
		{
			if (!this.bDisposed)
			{
				if (bDisposing)
				{
				}

				this.bDisposed = true;
			}
		}

		#endregion
	}
}
